<template>
  <div class="table-app-container schema-relations-page">
    <div class="fixed-table" style="border: 0;">
      <div class="fixed-table-actionbar">
        <div class="action-bar">
          <el-button type="primary" size="mini" icon="el-icon-plus" @click="onOpenEditRelation('')">添加关联</el-button>
        </div>
      </div>
      <PageableTable
        :loading="loading"
        :data="insideTableData"
        :total="0"
        :table-height="tableHeight"
        :disable-pageable="true"
        class="fixed-table-table"
      >
        <el-table-column v-for="column in insideColumns" :key="column.key" :width="column.width" :label="column.title" :prop="column.key" :sortable="column.sortable">
          <template slot-scope="scope">
            <column-render :render="column.render" :row="scope.row" :column="column" />
          </template>
        </el-table-column>
        <el-table-column label="操作" align="center" width="120" class-name="small-padding fixed-width">
          <template slot-scope="{row}">
            <el-button v-if="schemadef === row.src._id" title="编辑" type="text" size="mini" icon="el-icon-edit" @click="onOpenEditRelation(row._id)" />
            <el-button title="删除" type="text" size="mini" icon="el-icon-delete" @click="remove(row._id)" />
          </template>
        </el-table-column>
      </PageableTable>
    </div>
    <FormDialog title="关联维护" :show.sync="addRelaDialogVisible" width="50%" :drawer="true" @on-ok="onAddRelationOk" @on-cancel="onAddRelationCancel">
      <el-form v-if="addRelaDialogVisible" ref="addform" :model="addFormItem" :rules="rules" label-width="100px" size="mini">
        <input v-model="addFormItem._id" type="hidden">
        <el-form-item label="描述" prop="desc" required>
          <el-input v-model="addFormItem.desc" />
        </el-form-item>
        <el-form-item label="关联类型" prop="type" required>
          <el-select v-model="addFormItem.type" :disabled="!!addFormItem._id" placeholder="关联类型">
            <el-option v-for="item in relationtypes" :key="item._id" :label="item.name" :value="item._id" />
          </el-select>
        </el-form-item>
        <el-form-item label="源模型" prop="src" required>
          <el-select v-model="addFormItem.src" :disabled="!!addFormItem._id" filterable placeholder="源模型" @change="onSrcChanged">
            <el-option-group v-for="group in schemaGroups" :key="group.value" :label="group.label">
              <el-option v-for="item in group.children" :key="item.value" :label="item.label" :value="item.value" />
            </el-option-group>
          </el-select>
        </el-form-item>
        <el-form-item label="目标模型" prop="dest" required>
          <el-select v-model="addFormItem.dest" :disabled="!!addFormItem._id" filterable placeholder="目标模型" @change="onDestChanged">
            <el-option-group v-for="group in schemaGroups" :key="group.value" :label="group.label">
              <el-option v-for="item in group.children" :key="item.value" :label="item.label" :value="item.value" />
            </el-option-group>
          </el-select>
        </el-form-item>
        <el-form-item label="关系约束" prop="restrain" required>
          <rela-restrain-select v-model="addFormItem.restrain" :disabled="!!addFormItem._id" />
        </el-form-item>
        <el-form-item v-for="(attr, index) in addFormItem.attrs" :key="attr._dirty" :label="attr.name">
          <dynamic-attr-form v-model="addFormItem.attrs[index]" :used-datatypes="['number','bool','string']" @delete="onRemoveRelationAttr(addFormItem.attrs[index])" />
        </el-form-item>
        <el-form-item>
          <el-button type="text" icon="el-icon-plus" @click="onAddRelationAttr">添加属性</el-button>
        </el-form-item>
      </el-form>
    </FormDialog>
  </div>
</template>

<script>

import FormDialog from '@neu/biz-core/components/FormDialog'
import PageableTable from '@neu/biz-core/components/PageableTable'
import RelaRestrainSelect from '@neu/cmdb-ui/components/RelaRestrainSelect'
import { getRelations, addRelation, removeRelation, groups as schemaGroups } from '@neu/cmdb-ui/api/schema'
import { detail as relationDetail, update as updateRelation } from '@neu/cmdb-ui/api/relation'
import databind from '@neu/biz-core/mixins/databind'
import { debounce, deepClone, createUniqueString } from '@neu/biz-core/utils'
import { on } from '@neu/biz-core/utils/utils'
import ValidatorSchema from 'async-validator'
import DynamicAttrForm from '@neu/cmdb-ui/components/DynamicAttrForm'

const addFormItem = {
  type: '',
  src: '',
  dest: '',
  desc: '',
  restrain: 'onetomany',
  attrs: []
}

const rules = {
  desc: [
    { required: true, message: '描述必填项', trigger: 'blur' }
  ],
  type: [
    { required: true, message: '关联类型必填项', trigger: 'blur' }
  ],
  src: [
    { required: true, message: '源模型必填项', trigger: 'blur' }
  ],
  dest: [
    { required: true, message: '目标模型必填项', trigger: 'blur' }
  ],
  restrain: [
    { required: true, message: '关系约束必填项', trigger: 'blur' }
  ]
}

export default {
  name: 'RelationsPage',
  components: {
    FormDialog,
    PageableTable,
    RelaRestrainSelect,
    DynamicAttrForm
  },
  mixins: [databind],
  props: {
    schemadef: {
      required: true,
      type: String
    }
  },
  data() {
    return {
      schemaGroups: [],
      addFormItem: { ...addFormItem },
      rules: { ...rules },
      descriptor: {},
      addRelaDialogVisible: false,
      relationsLoaded: false,
      relations: [],
      r: getRelations(this.schemadef),
      d: removeRelation(this.schemadef),
      insideColumns: [
        {
          title: '关联类型',
          key: 'type.name',
          render: (h, params) => {
            return (
              <el-tooltip placement='top-start' effect='light'><div slot='content'>源模型描述：{ params.row.type.srcdesc }<br/>目标模型描述：{ params.row.type.destdesc }</div><span>{ params.row.type.name }</span></el-tooltip>
            )
          }
        },
        {
          title: '源模型',
          key: 'src.name',
          render: (h, params) => {
            return (
              <router-link to={'/cmdb/schemas/' + params.row.src._id} class={'el-link el-link--primary'}>{ params.row.src.name }</router-link>
            )
          }
        },
        {
          title: '目标模型',
          key: 'dest.name',
          render: (h, params) => {
            return (
              <router-link to={'/cmdb/schemas/' + params.row.dest._id} class={'el-link el-link--primary'}>{ params.row.dest.name }</router-link>
            )
          }
        },
        {
          title: '约束关系',
          key: 'restrain',
          render: (h, params) => {
            return (
              <span>{ this.renderRestrain(params.row.restrain) }</span>
            )
          }
        },
        { title: '描述', key: 'desc' }
      ]
    }
  },
  computed: {
    datatypes() {
      return this.$store.state.cmdb.datatypes
        .filter(it => it.value !== 'datasource' && it.value !== 'foreign' && it.value !== 'enum')
    },
    relationtypes() {
      return deepClone(this.$store.state.cmdb.relationtypes)
        .map(it => {
          it.name += '（源模型：' + it.srcdesc + ' 目标模型：' + it.destdesc + '）'
          return it
        })
    }
  },
  created() {
    schemaGroups().then(response => {
      this.schemaGroups = response.data
    })
    this.filter()
  },
  methods: {
    // 编辑和新建关联关系
    onOpenEditRelation(relationId) {
      if (relationId) {
        relationDetail(relationId).then(response => {
          const { _id, type, src, dest, desc, restrain } = response.data
          const attrs = response.data.attrs || []
          const mAttrs = attrs.map(a => { a._dirty = createUniqueString(); a._error = { name: false, field: false, dtype: false }; return a })
          const data = {
            _id,
            type: type._id,
            src: src._id,
            dest: dest._id,
            desc,
            restrain,
            attrs: mAttrs
          }
          this.addFormItem = { ...data }
          this.addRelaDialogVisible = true
        })
      } else {
        const data = {
          type: '',
          src: this.schemadef,
          dest: '',
          desc: '',
          restrain: 'onetoone',
          attrs: []
        }
        this.addFormItem = { ...data }
        this.addRelaDialogVisible = true
      }
    },

    // 保存编辑/新建表单事件
    onAddRelationOk() {
      const that = this
      this.$refs.addform.validate(valid => {
        if (!valid) {
          return
        }

        this.descriptor['attrs'] = {
          type: 'array',
          fields: {}
        }
        this.addFormItem.attrs.forEach((attr, idx) => {
          attr._error = { name: false, field: false, dtype: false }
          const attrRule = {
            type: 'object',
            required: true,
            fields: {
              name: [{
                type: 'string',
                required: true
              }, {
                validator: (rule, value, callback) => {
                  let count = 0
                  this.addFormItem.attrs.forEach(it => {
                    if (value && it.name === value) count++
                  })
                  if (count > 1) {
                    callback('属性名不能重复')
                  } else {
                    callback()
                  }
                }
              }],
              field: [{
                type: 'string',
                required: true
              }, {
                validator: (rule, value, callback) => {
                  let count = 0
                  this.addFormItem.attrs.forEach(it => {
                    if (value && it.field === value) count++
                  })
                  if (count > 1) {
                    callback('属性标识不能重复')
                  } else {
                    callback()
                  }
                }
              }],
              dtype: { type: 'string', required: true }
            }
          }
          this.descriptor['attrs'].fields[idx] = attrRule
        })
        var validator = new ValidatorSchema(this.descriptor)

        validator.validate(this.addFormItem, (errors, fields) => {
          if (errors) {
            errors.forEach(error => {
              const parts = error.field.split('.')
              const attrIndex = parts[1]
              const fieldName = parts[2]
              that.addFormItem.attrs[attrIndex]._error[fieldName] = true
            })
            return
          }

          if (this.addFormItem._id) {
            updateRelation(this.addFormItem._id, this.addFormItem).then(response => {
              this.filter()
              this.addFormItem = { ...addFormItem }
              this.addRelaDialogVisible = false
            })
          } else {
            addRelation(this.schemadef, this.addFormItem).then(response => {
              this.filter()
              this.addFormItem = { ...addFormItem }
              this.addRelaDialogVisible = false
            })
          }
        })
      })
    },

    // 取消编辑/新建表单事件
    onAddRelationCancel() {
      this.addFormItem = { ...addFormItem }
      this.addRelaDialogVisible = false
    },

    // 动态添加 关联关系 属性事件
    onAddRelationAttr() {
      // 防止重复添加
      if (this.addFormItem.attrs.some(attr => attr.field === '')) {
        return
      }
      this.addFormItem.attrs.push({
        _dirty: createUniqueString(),
        _error: { name: false, field: false, dtype: false },
        _inherit: false,
        name: '',
        field: '',
        dtype: '',
        len: 255,
        dvalue: '',
        notnullable: false,
        enums: [],
        datasource: {}
      })
    },

    // 源模型表单项改变事件，控制源模型和目标模型至少有一个是当前模型
    onSrcChanged(val) {
      if (val !== this.schemadef) {
        this.addFormItem.dest = this.schemadef
      }
    },

    // 目标模型表单项改变事件，控制源模型和目标模型至少有一个是当前模型
    onDestChanged(val) {
      if (val !== this.schemadef) {
        this.addFormItem.src = this.schemadef
      }
    },

    // 移除一个 关联关系 属性
    onRemoveRelationAttr(attr) {
      const idx = this.addFormItem.attrs.findIndex(a => a.field === attr.field)
      this.addFormItem.attrs.splice(idx, 1)
    },

    // render约束关系
    renderRestrain(restrain) {
      if (restrain === 'onetoone') {
        return '1:1'
      } else if (restrain === 'onetomany') {
        return '1:N'
      } else if (restrain === 'manytoone') {
        return 'N:1'
      } else if (restrain === 'manytomany') {
        return 'N:N'
      }
      return '无效约束'
    },

    // 自适应页面高度
    initListener() {
      this.$_resizeHandler = debounce(() => {
        const _this = this
        // 页面头高度 50
        // .app-container padding上下总和 16
        // document.querySelector('.table-app-container .fixed-table-filter').offsetHeight
        // document.querySelector('.table-app-container .fixed-table-actionbar')
        // 翻页组件高度 42
        // .fixed-table padding上下和边框 18
        const baseInfoSel = document.querySelector('.app-container .schema-edit-container .base-info')
        const baseInfoHeight = baseInfoSel ? baseInfoSel.offsetHeight : 0

        const tabsSel = document.querySelector('.el-tabs__header')
        const tabsHeight = tabsSel ? tabsSel.offsetHeight + 15 : 0

        const tableFilterSel = document.querySelector('.table-app-container .fixed-table-filter')
        const tableFilterHeight = tableFilterSel ? tableFilterSel.offsetHeight : 0

        const tableActionbarSel = document.querySelector('.table-app-container .fixed-table-actionbar')
        const tableActionbarHeight = tableActionbarSel ? tableActionbarSel.offsetHeight : 0

        const assetTableBarSel = document.querySelector('.table-app-container .asset-table-bar')
        const assetTableBarHeight = assetTableBarSel ? assetTableBarSel.offsetHeight : 0

        const filterTreeSel = document.querySelector('.table-app-container .filter-tree')
        if (filterTreeSel) {
          filterTreeSel.style.height = (window.innerHeight - 50 - 16) + 'px'
        }

        _this.tableHeight = window.innerHeight - 50 - 16 - baseInfoHeight - tabsHeight - tableFilterHeight - assetTableBarHeight - tableActionbarHeight - 22
      }, 300)

      on(window, 'resize', this.$_resizeHandler)
    }
  }
}
</script>

<style lang="scss">
.schema-relations-page {
  .validate-error {
    input {
      border-color: #ff4949;
    }
  }
}
</style>
